package draw.impl;

import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;

import draw.spec.TimeException;

/**
 * Like how a StaticStroke is a collection of Points, an
 * AnimatedStroke is a collection of Paths. A Stroke is a digital
 * representation of an actual brush stroke.
 *
 * @author Matthew B. Gray <matthew.b.gray@gmail.com>
 * @version 1.0
 */
public class AnimatedStroke implements Comparable, Iterable {
	private List<Path> paths;
	private StaticStroke start, finish;

	/**
	 * Constructs a new empty AnimatedStroke. This method is
	 * private so you can't use it outside of this class.
	 * @see this{@link #clone()}
	 */
	private AnimatedStroke() { paths = new LinkedList<Path>(); }

	public void addPath(Path p) { paths.add(p); }
	public Iterator<Path> iterator() { return paths.iterator(); }
	/** Returns an integer based on the order the "start" stroke was created. */
	public int compareTo(Object that) { return this.start.compareTo(((AnimatedStroke)that).start); }

	public AnimatedStroke(StaticStroke start, StaticStroke finish) {
		this(); // Default constructor
		this.start = start;
		this.finish = finish;

		// Warn if start or finish is already used.
		if(start.isUsed()) {
			System.err.println("Warning, re-using StaticStrokes start: ");
			System.err.println(start);
		}
		if(finish.isUsed()) {
			System.err.println("Warning, re-using StaticStrokes finish: ");
			System.err.println(finish);
		}

		this.start.setUsed();
		this.finish.setUsed();

		// points to the larger and the smaller of the two strokes.
		Iterator<Point> larger,smaller;
		// Find the ratio, e.g. 3:9 -> 1:3; cealing (larger / smaller)
		Integer ratioOneTo;

		if(start.size() > finish.size()) {
			ratioOneTo = Double.valueOf(
					Math.ceil(start.size() / finish.size()))
					.intValue();
			larger = start.iterator();
			smaller = finish.iterator();
		}
		else { // start.size() <= finish.size()
			ratioOneTo = Double.valueOf(
					Math.ceil(finish.size() / start.size()))
					.intValue();
			larger = finish.iterator();
			smaller = start.iterator();
		}

		Point currSmall, currLarge;
		int i=0;
		while(smaller.hasNext()) {
			currSmall = smaller.next();
			while(larger.hasNext() && i++ < ratioOneTo) {
				currLarge = larger.next();
				this.addPath(new Path(currSmall,currLarge));
			}
		}
	}

	public StaticStroke getState(Double time) throws TimeException {
		if(time < 0 || 1 < time)
			throw new TimeException("Please keep time sane.");

		StaticStroke toReturn = new StaticStroke();

		for(Path p : paths)
			toReturn.add(p.getState(time));

		return toReturn;
	}

	public String toString() {
		StringBuffer buff = new StringBuffer("Animated Stroke:\n");
		for(Path p : paths) {
			buff.append(p.toString()).append('\n');
		}
		return buff.toString();
	}

	public Object clone() {
		AnimatedStroke toReturn = new AnimatedStroke();
		toReturn.start = (StaticStroke)this.start.clone();
		toReturn.finish = (StaticStroke)this.finish.clone();
		toReturn.paths.addAll(this.paths);
		return toReturn;
	}
}
